#ifndef VISITOR_HPP_INCLUDED
#define VISITOR_HPP_INCLUDED

#include "irtree.hpp"
#include <string>
#include <vector>

#define VARIABLE_ARRAY_LEN 10000
#define MAX_SCOPE 200
#define IRTESEQ 10

using namespace std;

class IDSymbolTable;
class IDSymbol;
class ClassSymbol;
class MethodSymbol;
class ParameterSymbol;
class VariableSymbol;
class VariableSymbolList;
class AttributeSymbol;
class node_Program;
class node_ClassList;
class node_VarList;
class node_MethodList;
class node_NameDecl;
class node_Type;
class node_StmtList;
class node_While;
class node_If;
class node_Return;
class node_Read;
class node_Println;
class node_Assign;
class node_Call;
class node_ExpList;
class node_Name;
class node_New;
class node_Sign;
class node_Not;
class node_BitwiseOP;
class node_BooleanOP;
class node_BinOp;
class node_Bin2Op;
class node_RelationalOP;
class node_Number;
class node_Literal;
class node_Logic;
class StmtFragmentList;
class Access_List;
class Label;
class Temp;
class Label_List;
class Temp_List;
class FragmentList;
class Method;
class Literal_Frag;
class InFrame;
class InReg;
class FrameMIPS;
class IRNStmList;
class IRNExpList;
class IRNConst;
class IRNName;
class IRNTemp;
class IRNBinop;
class IRNMem;
class IRNCall;
class IRNEseq;
class IRNMove;
class IRNExpStm;
class IRNJump;
class IRNCJump;
class IRNSeq;
class IRNLabel;



/********************************************************************
*                  Class Visitor :: classe abstrata                 *
********************************************************************/
class Visitor{
public:
    virtual int visit(node_Program *p) = 0;
    virtual int visit(node_ClassList *cl) = 0;
    virtual int visit(node_VarList *vl) = 0;
    virtual int visit(node_MethodList *ml) = 0;
    virtual int visit(node_NameDecl *nd) = 0;
    virtual int visit(node_Type *t) = 0;
    virtual int visit(node_StmtList *sl) = 0;
    virtual int visit(node_While *w) = 0;
    virtual int visit(node_If *i) = 0;
    virtual int visit(node_Return *r) = 0;
    virtual int visit(node_Read *r) = 0;
    virtual int visit(node_Println *p) = 0;
    virtual int visit(node_Assign *a) = 0;
    virtual int visit(node_Call *c) = 0;
    virtual int visit(node_ExpList *el) = 0;
    virtual int visit(node_Name *n) = 0;
    virtual int visit(node_New *n) = 0;
    virtual int visit(node_Sign *s) = 0;
    virtual int visit(node_Not *n) = 0;
    virtual int visit(node_BitwiseOP *b) = 0;
    virtual int visit(node_BooleanOP *b) = 0;
    virtual int visit(node_BinOp *b) = 0;
    virtual int visit(node_Bin2Op *b) = 0;
    virtual int visit(node_RelationalOP *r) = 0;
    virtual int visit(node_Number *n) = 0;
    virtual int visit(node_Literal *l) = 0;
    virtual int visit(node_Logic *l) = 0;
};


/*********************************************************************
*   Class VisitorASTprint :: classe que especializa Class Visitor    *
**********************************************************************
* - a instacia desta classe será usada para imprimir a AST construída*
*   no Parser;                                                       *
*********************************************************************/
class VisitorASTprint : public Visitor{
public:
    unsigned int tab;
public:
    VisitorASTprint();
    void putTab();
    int visit(node_Program *p);
    int visit(node_ClassList *cl);
    int visit(node_VarList *vl);
    int visit(node_MethodList *ml);
    int visit(node_NameDecl *nd);
    int visit(node_Type *t);
    int visit(node_StmtList *sl);
    int visit(node_While *w);
    int visit(node_If *i);
    int visit(node_Return *r);
    int visit(node_Read *r);
    int visit(node_Println *p);
    int visit(node_Assign *a);
    int visit(node_Call *c);
    int visit(node_ExpList *el);
    int visit(node_Name *n);
    int visit(node_New *n);
    int visit(node_Sign *s);
    int visit(node_Not *n);
    int visit(node_BitwiseOP *b);
    int visit(node_BooleanOP *b);
    int visit(node_BinOp *b);
    int visit(node_Bin2Op *b);
    int visit(node_RelationalOP *r);
    int visit(node_Number *n);
    int visit(node_Literal *l);
    int visit(node_Logic *l);
};

class VisitorSemantic : public Visitor{
private:
    ClassSymbol* currentClass;
    MethodSymbol* currentMethod;
    ParameterSymbol* currentParameter;
    AttributeSymbol* currentAttribute;
    int declType;
    int arrayIdx;
    VariableSymbolList* variableArray[VARIABLE_ARRAY_LEN];
    IDSymbol *lastName;
    node_Name *lastNameAssign;
    int lastNameSymbolType;
    bool hasReturn;
    bool inNew;
public:
    VisitorSemantic();
    ~VisitorSemantic();
    void setLastNameValues(node_Name* nameAssign, IDSymbol* auxLastName, int auxLastNameSType);
    int visit(node_Program *p);
    int visit(node_ClassList *cl);
    int visit(node_VarList *vl);
    int visit(node_MethodList *ml);
    int visit(node_NameDecl *nd);
    int visit(node_Type *t);
    int visit(node_StmtList *sl);
    int visit(node_While *w);
    int visit(node_If *i);
    int visit(node_Return *r);
    int visit(node_Read *r);
    int visit(node_Println *p);
    int visit(node_Assign *a);
    int visit(node_Call *c);
    int visit(node_ExpList *el);
    int visit(node_Name *n);
    int visit(node_New *n);
    int visit(node_Sign *s);
    int visit(node_Not *n);
    int visit(node_BitwiseOP *b);
    int visit(node_BooleanOP *b);
    int visit(node_BinOp *b);
    int visit(node_Bin2Op *b);
    int visit(node_RelationalOP *r);
    int visit(node_Number *n);
    int visit(node_Literal *l);
    int visit(node_Logic *l);
};

class VisitorIRTGen : public Visitor{
public:
    char* currentClassName;
    FragmentList* fragmentList;
    FrameMIPS* mainMethodFrame;
    Method* mainMethod;
    Method* currentMethod;
    int declType;
    bool isArrayLenExp;
    bool isDestination;
    vector<ClassSymbol*> classHierarchy;
    IRNExp* thisPointer;
    int scopeLevel;
    int numVarDef[MAX_SCOPE]; //Armazena o numero de declarações de variável em cada nível de escopo
    vector<Local_Access*> restrictScopeVars;
public:
    VisitorIRTGen();
    ~VisitorIRTGen();
    int visit(node_Program *p);
    int visit(node_ClassList *cl);
    int visit(node_VarList *vl);
    int visit(node_MethodList *ml);
    int visit(node_NameDecl *nd);
    int visit(node_Type *t);
    int visit(node_StmtList *sl);
    int visit(node_While *w);
    int visit(node_If *i);
    int visit(node_Return *r);
    int visit(node_Read *r);
    int visit(node_Println *p);
    int visit(node_Assign *a);
    int visit(node_Call *c);
    int visit(node_ExpList *el);
    int visit(node_Name *n);
    int visit(node_New *n);
    int visit(node_Sign *s);
    int visit(node_Not *n);
    int visit(node_BitwiseOP *b);
    int visit(node_BooleanOP *b);
    int visit(node_BinOp *b);
    int visit(node_Bin2Op *b);
    int visit(node_RelationalOP *r);
    int visit(node_Number *n);
    int visit(node_Literal *l);
    int visit(node_Logic *l);
};

/********************************************************************
*               Class VisitorIRT :: classe abstrata                 *
********************************************************************/
class VisitorIRT{
public:
    virtual int visit(StmtFragmentList *sfl) = 0;
    virtual int visit(Access_List *al) = 0;
    virtual int visit(Label *l) = 0;
    virtual int visit(Temp *t) = 0;
    virtual int visit(Label_List *ll) = 0;
    virtual int visit(Temp_List *tl) = 0;
    virtual int visit(FragmentList *fl) = 0;
    virtual int visit(Method *m) = 0;
    virtual int visit(Literal_Frag *lf) = 0;
    virtual int visit(InFrame *i) = 0;
    virtual int visit(InReg *i) = 0;
    virtual int visit(FrameMIPS *f) = 0;
    virtual int visit(IRNStmList *sl) = 0;
    virtual int visit(IRNExpList *el) = 0;
    virtual int visit(IRNConst *c) = 0;
    virtual int visit(IRNConstf *c) = 0;
    virtual int visit(IRNName *n) = 0;
    virtual int visit(IRNTemp *t) = 0;
    virtual int visit(IRNBinop *b) = 0;
    virtual int visit(IRNMem *m) = 0;
    virtual int visit(IRNCall *el) = 0;
    virtual int visit(IRNEseq *c) = 0;
    virtual int visit(IRNMove *n) = 0;
    virtual int visit(IRNExpStm *es) = 0;
    virtual int visit(IRNJump *j) = 0;
    virtual int visit(IRNCJump *cj) = 0;
    virtual int visit(IRNSeq *s) = 0;
    virtual int visit(IRNLabel *l) = 0;
};

class VisitorIRTPrint : public VisitorIRT{
public:
    int tab;
public:
    VisitorIRTPrint();
    ~VisitorIRTPrint();
    void putTab();
    int visit(StmtFragmentList *sfl);
    int visit(Access_List *al);
    int visit(Label *l);
    int visit(Temp *t);
    int visit(Label_List *ll);
    int visit(Temp_List *tl);
    int visit(FragmentList *fl);
    int visit(Method *m);
    int visit(Literal_Frag *lf);
    int visit(InFrame *i);
    int visit(InReg *i);
    int visit(FrameMIPS *f);
    int visit(IRNStmList *sl);
    int visit(IRNExpList *el);
    int visit(IRNConst *c);
    int visit(IRNConstf *c);
    int visit(IRNName *n);
    int visit(IRNTemp *t);
    int visit(IRNBinop *b);
    int visit(IRNMem *m);
    int visit(IRNCall *el);
    int visit(IRNEseq *c);
    int visit(IRNMove *n);
    int visit(IRNExpStm *es);
    int visit(IRNJump *j);
    int visit(IRNCJump *cj);
    int visit(IRNSeq *s);
    int visit(IRNLabel *l);
};

class VisitorIRTCanonizer{
public:
    bool changed;
public:
    VisitorIRTCanonizer();
    void visit(StmtFragmentList *sfl);
    void visit(Access_List *al);
    void visit(Label *l);
    void visit(Temp *t);
    void visit(Label_List *ll);
    void visit(Temp_List *tl);
    void visit(FragmentList *fl);
    void visit(Method *m);
    void visit(Literal_Frag *lf);
    void visit(InFrame *i);
    void visit(InReg *i);
    void visit(FrameMIPS *f);
    void visit(IRNStmList *sl);
    void visit(IRNExpList *el);
    IRNExp* visit(IRNConst *c);
    IRNExp* visit(IRNConstf *c);
    IRNExp* visit(IRNName *n);
    IRNExp* visit(IRNTemp *t);
    IRNExp* visit(IRNBinop *b);
    IRNExp* visit(IRNMem *m);
    IRNExp* visit(IRNCall *el);
    IRNExp* visit(IRNEseq *c);
    IRNStm* visit(IRNMove *n);
    IRNStm* visit(IRNExpStm *es);
    IRNStm* visit(IRNJump *j);
    IRNStm* visit(IRNCJump *cj);
    IRNStm* visit(IRNSeq *s);
    IRNStm* visit(IRNLabel *l);
};

class VisitorIRTAsmTranslator : public VisitorIRT{
public:
    int frameSize;
    vector<string> asmCode;
    vector<Temp*> availableTemps;
    bool isLiteral;
public:
    VisitorIRTAsmTranslator();
    ~VisitorIRTAsmTranslator();
    int visit(StmtFragmentList *sfl);
    int visit(Access_List *al);
    int visit(Label *l);
    int visit(Temp *t);
    int visit(Label_List *ll);
    int visit(Temp_List *tl);
    int visit(FragmentList *fl);
    int visit(Method *m);
    int visit(Literal_Frag *lf);
    int visit(InFrame *i);
    int visit(InReg *i);
    int visit(FrameMIPS *f);
    int visit(IRNStmList *sl);
    int visit(IRNExpList *el);
    int visit(IRNConst *c);
    int visit(IRNConstf *c);
    int visit(IRNName *n);
    int visit(IRNTemp *t);
    int visit(IRNBinop *b);
    int visit(IRNMem *m);
    int visit(IRNCall *el);
    int visit(IRNEseq *c);
    int visit(IRNMove *n);
    int visit(IRNExpStm *es);
    int visit(IRNJump *j);
    int visit(IRNCJump *cj);
    int visit(IRNSeq *s);
    int visit(IRNLabel *l);
};

#endif // VISITOR_HPP_INCLUDED
